/**
 * Yolema.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */
package org.grayrabbit.pay.core.struts2;

import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.PageContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.VelocityResult;
import org.apache.struts2.views.JspSupportServlet;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * 支持对Velocity Layout的显示
 * 
 * @author wy
 * @version $Id: VelocityLayoutResult.java, v 0.1 2012-5-29 下午3:14:50 wy Exp $
 */
public class VelocityLayoutResult extends VelocityResult {

    /**serialVersionUID*/
    private static final long  serialVersionUID         = 6390435527609000839L;

    private static final Log   LOG                      = LogFactory.getLog(VelocityLayoutResult.class);
    public static final String KEY_SCREEN_CONTENT       = "screen_content";
    public static final String KEY_LAYOUT               = "layout";
    public static final String PROPERTY_DEFAULT_LAYOUT  = "tools.view.servlet.layout.default.template";
    public static final String PROPERTY_LAYOUT_DIR      = "tools.view.servlet.layout.directory";
    public static final String PROPERTY_INPUT_ENCODING  = "input.encoding";
    public static final String PROPERTY_OUTPUT_ENCODING = "output.encoding";
    public static final String PROPERTY_CONTENT_TYPE    = "default.contentType";

    protected VelocityManager  velocityManager;
    protected String           defaultLayout;
    protected String           layoutDir;
    protected String           inputEncoding;
    protected String           outputEncoding;
    protected String           contentType;

    public VelocityLayoutResult() {
        super();
    }

    public VelocityLayoutResult(String location) {
        super(location);
    }

    @Inject
    @Override
    public void setVelocityManager(VelocityManager mgr) {
        super.setVelocityManager(mgr);
        if (mgr != null && velocityManager == null) {
            this.velocityManager = mgr;
            ServletContext servletContext = ServletActionContext.getServletContext();
            velocityManager.init(servletContext);

            defaultLayout = (String) velocityManager.getVelocityEngine().getProperty(
                PROPERTY_DEFAULT_LAYOUT);
            layoutDir = (String) velocityManager.getVelocityEngine().getProperty(
                PROPERTY_LAYOUT_DIR);
            if (!layoutDir.endsWith("/")) {
                layoutDir += '/';
            }

            if (!layoutDir.startsWith("/")) {
                layoutDir = "/" + layoutDir;
            }

            // for efficiency's sake, make defaultLayout a full path now
            defaultLayout = layoutDir + defaultLayout;

            inputEncoding = (String) velocityManager.getVelocityEngine().getProperty(
                PROPERTY_INPUT_ENCODING);
            outputEncoding = (String) velocityManager.getVelocityEngine().getProperty(
                PROPERTY_OUTPUT_ENCODING);
            contentType = (String) velocityManager.getVelocityEngine().getProperty(
                PROPERTY_CONTENT_TYPE);

            if (outputEncoding != null && contentType != null) {
                contentType = contentType + ";charset=" + outputEncoding;
            }

        }
    }

    @Override
    protected String getEncoding(String templateLocation) {
        return inputEncoding;
    }

    @Override
    public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
        ValueStack stack = ActionContext.getContext().getValueStack();

        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();
        JspFactory jspFactory = null;
        Servlet servlet = JspSupportServlet.jspSupportServlet;

        boolean usedJspFactory = false;
        PageContext pageContext = (PageContext) ActionContext.getContext().get(
            ServletActionContext.PAGE_CONTEXT);

        if (pageContext == null && servlet != null) {
            jspFactory = JspFactory.getDefaultFactory();
            pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192,
                true);
            ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext);
            usedJspFactory = true;
        }

        try {
            Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation,
                finalLocation, inputEncoding);
            Context context = createContext(velocityManager, stack, request, response,
                finalLocation);
            //将页面执行结果写入到字符串缓存中
            StringWriter sw = new StringWriter();
            t.merge(context, sw);
            context.put(KEY_SCREEN_CONTENT, sw.toString());

            Object obj = context.get(KEY_LAYOUT);
            String layout = (obj == null) ? null : obj.toString();
            if (layout == null) {
                layout = defaultLayout;
            } else {
                layout = layoutDir + layout;
            }

            Template layout_vm = null;
            try {
                layout_vm = getTemplate(stack, velocityManager.getVelocityEngine(), invocation,
                    layout, inputEncoding);
            } catch (Exception e) {
                LOG.error("VelocityLayoutResult: Can't load layout \"" + layout + "\": ", e);
                if (!layout.equals(defaultLayout)) {
                    layout_vm = getTemplate(stack, velocityManager.getVelocityEngine(), invocation,
                        defaultLayout, inputEncoding);
                }
            }

            Writer writer = new OutputStreamWriter(response.getOutputStream(), outputEncoding);
            response.setContentType(contentType);

            layout_vm.merge(context, writer);

            writer.flush();
            writer.close();
        } catch (Exception e) {
            LOG.error("Unable to render Velocity Template, '" + finalLocation + "'", e);
            throw e;
        } finally {
            if (usedJspFactory) {
                jspFactory.releasePageContext(pageContext);
            }
        }
    }

}
